%%-*- mode: erlang -*-
%% ex: ft=erlang

%% @doc Specify the address and port of the bridge to connect to. Several 
%% bridges can configured by using different bridge names (e.g. br0). If the 
%% connection supports SSL encryption bridge.ssl.<name> can be used.
{mapping, "vmq_bridge.tcp.$name", "vmq_bridge.config", [
                                                    {datatype, string},
                                                    {include_default, "br0"},
                                                    {commented, "127.0.0.1:1889"}
                                                   ]}. 
{mapping, "vmq_bridge.ssl.$name", "vmq_bridge.config", [
                                                    {datatype, string},
                                                    hidden
                                                   ]}. 

%% @doc Set the clean session option for the bridge. By default this is disabled, 
%% which means that all subscriptions on the remote broker are kept in case of 
%% the network connection dropping. If enabled, all subscriptions and messages 
%% on the remote broker will be cleaned up if the connection drops. 
{mapping, "vmq_bridge.tcp.$name.cleansession", "vmq_bridge.config", [
                                                                 {default, off},
                                                                 {datatype, flag},
                                                                 {include_default, "br0"},
                                                                 {commented, off}
                                                                ]}. 
{mapping, "vmq_bridge.ssl.$name.cleansession", "vmq_bridge.config", [
                                                       {default, off},
                                                       {datatype, flag},
                                                       hidden
                                                      ]}. 

%% @doc Set the client id for this bridge connection. If not defined, this 
%% defaults to 'name.hostname', where name is the connection name and hostname 
%% is the hostname of this computer.
{mapping, "vmq_bridge.tcp.$name.client_id", "vmq_bridge.config", [
                                                              {default, "auto"},
                                                              {datatype, string},
                                                              {include_default, "br0"},
                                                              {commented, "auto"}
                                                             ]}. 
{mapping, "vmq_bridge.ssl.$name.client_id", "vmq_bridge.config", [
                                                       {default, "auto"},
                                                       {datatype, string},
                                                       hidden
                                                      ]}. 

%% @doc Set the mountpoint for this bridge connection. If not defined, this 
%% defaults to the default mountpoint.

{mapping, "vmq_bridge.tcp.$name.mountpoint", "vmq_bridge.config", [
                                                              {default, ""},
                                                              {datatype, string},
                                                              {include_default, "br0"},
                                                              {commented, ""}
                                                             ]}. 
{mapping, "vmq_bridge.ssl.$name.mountpoint", "vmq_bridge.config", [
                                                       {default, ""},
                                                       {datatype, string},
                                                       hidden
                                                      ]}. 

%% @doc Set the number of seconds after which the bridge should send a ping if 
%% no other traffic has occurred.
{mapping, "vmq_bridge.tcp.$name.keepalive_interval", "vmq_bridge.config", [
                                                                       {default, 60},
                                                                       {datatype, integer},
                                                                       {include_default, "br0"},
                                                                       {commented, 60}
                                                                      ]}. 
{mapping, "vmq_bridge.ssl.$name.keepalive_interval", "vmq_bridge.config", [
                                                       {default, 60},
                                                       {datatype, integer},
                                                       hidden
                                                      ]}. 

%% @doc Set the persistent queue option for this bridge. By default this is off, 
%% meaning the queued messages are stored in memory only. If set to on, 
%% queued messages will be stored persistently on disk. If you're using 
%% persistency, make sure to configure the queue_dir option.
{mapping, "vmq_bridge.tcp.$name.persistent_queue", "vmq_bridge.config", [
                                                                 {default, off},
                                                                 {datatype, flag},
                                                                 {include_default, "br0"},
                                                                 {commented, off}
                                                                ]}. 
{mapping, "vmq_bridge.ssl.$name.persistent_queue", "vmq_bridge.config", [
                                                       {default, off},
                                                       {datatype, flag},
                                                       hidden
                                                      ]}. 

%% @doc Set the directory where queued messages for this bridge will be stored, if persistent_queue is set to on.
%% It is recommended to set this to a unique directory. If you're using multiple persistent bridge instances, 
%% then omitting this option can lead to serious problems. 
{mapping, "vmq_bridge.tcp.$name.queue_dir", "vmq_bridge.config", [
                                                             {datatype, directory},
                                                             {include_default, "br0"},
                                                             {commented, "/qdata/br0"}
                                                            ]}. 
{mapping, "vmq_bridge.ssl.$name.queue_dir", "vmq_bridge.config", [
                                                       {datatype, directory},
                                                       {include_default, "sbr0"},
                                                       hidden
                                                      ]}. 

%% @doc Configure a username for the bridge. This is used for authentication 
%% purposes when connecting to a broker that support MQTT v3.1 and requires a 
%% username and/or password to connect. See also the password option.
{mapping, "vmq_bridge.tcp.$name.username", "vmq_bridge.config", [
                                                             {datatype, string},
                                                             {include_default, "br0"},
                                                             {commented, "my_remote_user"}
                                                            ]}. 
{mapping, "vmq_bridge.ssl.$name.username", "vmq_bridge.config", [
                                                       {datatype, string},
                                                       {include_default, "sbr0"},
                                                       hidden
                                                      ]}. 

%% @doc Configure a password for the bridge. This is used for authentication 
%% purposes when connecting to a broker that support MQTT v3.1 and requires a 
%% username and/or password to connect. This option is only valid if a username 
%% is also supplied.
{mapping, "vmq_bridge.tcp.$name.password", "vmq_bridge.config", [
                                                             {datatype, string},
                                                             {include_default, "br0"},
                                                             {commented, "my_remote_password"}
                                                            ]}. 
{mapping, "vmq_bridge.ssl.$name.password", "vmq_bridge.config", [
                                                       {datatype, string},
                                                       {include_default, "sbr0"},
                                                       hidden
                                                      ]}.

%% @doc Configure segment size (in Bytes) for the persistent queue.
{mapping, "vmq_bridge.tcp.$name.segment_size", "vmq_bridge.config", [
                                                             {default, 4096},
                                                             {datatype, bytesize},
                                                             {include_default, "br0"},
                                                             {commented, 4096}
                                                            ]}. 
{mapping, "vmq_bridge.ssl.$name.segment_size", "vmq_bridge.config", [
                                                       {default, 4096},
                                                       {datatype, bytesize},
                                                       {include_default, "sbr0"},
                                                       hidden
                                                      ]}.

%% @doc Number of messages that will be read from Queue and published at once.
%% Only after the entire batch has been completed (e.g. all messages were pubacked if QoS 1),
%% will the next batch be read. Set this to a lower setting if  you encounter bandwidth problems. 
{mapping, "vmq_bridge.tcp.$name.outgoing_batch_size", "vmq_bridge.config", [
                                                             {default, 100},
                                                             {datatype, integer},
                                                             {include_default, "br0"},
                                                             {commented, 100}
                                                            ]}. 
{mapping, "vmq_bridge.ssl.$name.outgoing_batch_size", "vmq_bridge.config", [
                                                       {default, 100},
                                                       {datatype, integer},
                                                       {include_default, "sbr0"},
                                                       hidden
                                                      ]}.

%% @doc Provide a Server Name Indication for an SSL endpoint
{mapping, "vmq_bridge.ssl.$name.sni", "vmq_bridge.config", [
                                                       {datatype, string},
                                                       {include_default, "sbr0"},
                                                       hidden
                                                      ]}.                                             

%% @doc Define one or more topic pattern to be shared between the two brokers. 
%% Any topics matching the pattern (including wildcards) are shared. 
%% The following format is used:
%%
%%  pattern [[[ out | in | both ] qos-level] local-prefix remote-prefix]
%%
%%  [ out | in | both ]: specifies that this bridge exports messages (out), imports
%%  messages (in) or shared in both directions (both). If undefined we default to 
%%  export (out).
%%
%%  qos-level: specifies the publish/subscribe QoS level used for this
%%  toppic. If undefined we default to QoS 0.
%%
%%  local-prefix and remote-prefix: For incoming topics, the bridge
%%  will prepend the pattern with the remote prefix and subscribe to
%%  the resulting topic on the remote broker.  When a matching
%%  incoming message is received, the remote prefix will be removed
%%  from the topic and then the local prefix added.
%%
%%  For outgoing topics, the bridge will prepend the pattern with the
%%  local prefix and subscribe to the resulting topic on the local
%%  broker. When an outgoing message is processed, the local prefix
%%  will be removed from the topic then the remote prefix added.
%%
%%  For shared subscriptions topic prefixes are applied only to the
%%  topic part of the subscription.
{mapping, "vmq_bridge.tcp.br0.topic.1", "vmq_bridge.config", [
                                                              {datatype, string},
                                                              {commented, "topic"}
                                                             ]}.

{mapping, "vmq_bridge.tcp.$name.topic.$topic_id", "vmq_bridge.config", [
                                                                        {datatype, string},
                                                                        {include_default, "1"},
                                                                        hidden
                                                                       ]}.

{mapping, "vmq_bridge.ssl.$name.topic.$topic_id", "vmq_bridge.config", [
                                                       {datatype, string},
                                                       hidden
                                                      ]}. 

%% @doc Set the amount of time a bridge using the automatic start type will wait 
%% until attempting to reconnect. Defaults to 30 seconds.
{mapping, "vmq_bridge.tcp.$name.restart_timeout", "vmq_bridge.config", [
                                                                    {datatype, integer},
                                                                    {default, 10},
                                                                    {include_default, "br0"},
                                                                    {commented, 10}
                                                                   ]}.
{mapping, "vmq_bridge.ssl.$name.restart_timeout", "vmq_bridge.config", [
                                                                       {datatype, integer},
                                                                       {default, 10},
                                                                       hidden
                                                                      ]}.

%% @doc If try_private is enabled, the bridge will attempt to indicate to the 
%% remote broker that it is a bridge not an ordinary client. 
%% Note that loop detection for bridges is not yet implemented. 
{mapping, "vmq_bridge.tcp.$name.try_private", "vmq_bridge.config", [
                                                                {datatype, flag},
                                                                {default, on},
                                                                {include_default, "br0"},
                                                                {commented, on}
                                                               ]}.
{mapping, "vmq_bridge.ssl.$name.try_private", "vmq_bridge.config", [
                                                                   {datatype, flag},
                                                                   {default, on},
                                                                   hidden
                                                                  ]}.

%% @doc Set the MQTT protocol version to be used by the bridge.
{mapping, "vmq_bridge.tcp.$name.mqtt_version", "vmq_bridge.config", [
                                                                     {datatype, {enum, ['3', '4', '5']}},
                                                                     {default, '3'},
                                                                     {include_default, "br0"},
                                                                     {commented, '3'}
                                                                    ]}.
{mapping, "vmq_bridge.ssl.$name.mqtt_version", "vmq_bridge.config", [
                                                                     {datatype, {enum, ['3', '4', '5']}},
                                                                     {default, '3'},
                                                                     hidden
                                                                    ]}.

%% @doc Maximum number of outgoing messages the bridge will buffer
%% while not connected to the remote broker. Messages published while
%% the buffer is full are dropped. A value of 0 means buffering is
%% disabled.
{mapping, "vmq_bridge.tcp.$name.max_outgoing_buffered_messages", "vmq_bridge.config", [
                                                                {datatype, integer},
                                                                {default, 0},
                                                                {include_default, "br0"},
                                                                {commented, 0}
                                                               ]}.

{mapping, "vmq_bridge.ssl.$name.max_outgoing_buffered_messages", "vmq_bridge.config", [
                                                                   {datatype, integer},
                                                                   {default, 0},
                                                                   hidden
                                                                  ]}.

%% @doc Percentage of max_outgoing_buffered_messages that will be used for the pubrel queue. 
%% The pubrel queue is only relevant, when bridging topics with QoS 2. In that case it is highly recommended,
%% to set this option. Allowed values: 0-100, e.g. value of 10 means 10%.
%% If this is set to 0, the pubrel queue will be disabled (default).
%% Caution: the segment_size should not be bigger than max_outgoing_buffered_messages * (pubrel_queue_ratio/100).
{mapping, "vmq_bridge.tcp.$name.pubrel_queue_ratio", "vmq_bridge.config", [
                                                                    {datatype, {percent, integer}},
                                                                    {default, 0},
                                                                    {include_default, "br0"},
                                                                    {commented, 10}
                                                                   ]}.
%% @doc For outgoing bridges, let us know whether the other endpoint is
%% 'inet' or 'inet6' (an IPv4 or an IPv6 address)
{mapping, "vmq_bridge.tcp.$name.inet_version", "vmq_bridge.config", [
                                                         {datatype, {enum, ['inet', 'inet6']}},
                                                            {default, 'inet'},
                                                            hidden
                                                        ]}.
{mapping, "vmq_bridge.ssl.$name.pubrel_queue_ratio", "vmq_bridge.config", [
                                                                       {datatype, {percent, integer}},
                                                                       {default, 0},
                                                                       hidden
                                                                      ]}.

%% @doc The cafile is used to define the path to a file containing
%% the PEM encoded CA certificates that are trusted. 
{mapping, "vmq_bridge.ssl.$name.cafile", "vmq_bridge.config", [
                                                               {default, ""},
                                                               {datatype, file},
                                                               {include_default, "sbr0"},
                                                               {commented, "{{platform_etc_dir}}/cacerts.pem"},
                                                               {validators, ["file-exists", "file-is-readable"]}

                                                             ]}. 

%% @doc Define the path to a folder containing 
%% the PEM encoded CA certificates that are trusted. 
{mapping, "vmq_bridge.ssl.$name.capath", "vmq_bridge.config", [
                                                               %% TODO: this option isn't implemented in the bridge and should be fully removed on VerneMQ 2.0
                                                               {default, ""},
                                                               {datatype, file},
                                                               {include_default, "sbr0"},
                                                               {commented, "{{platform_etc_dir}}/cacerts"},
                                                               hidden
                                                              ]}.

%% @doc Set the path to the PEM encoded server certificate.
{mapping, "vmq_bridge.ssl.$name.certfile", "vmq_bridge.config", [
                                                                 {default, ""},
                                                                 {datatype, file},
                                                                 {include_default, "sbr0"},
                                                                 {commented, "{{platform_etc_dir}}/cert.pem"},
                                                                 {validators, ["file-exists", "file-is-readable"]}
                                                                ]}.
%% @doc Set the path to the PEM encoded key file.
{mapping, "vmq_bridge.ssl.$name.keyfile", "vmq_bridge.config", [
                                                                {default, ""},
                                                                {datatype, file},
                                                                {include_default, "sbr0"},
                                                                {commented, "{{platform_etc_dir}}/key.pem"},
                                                                {validators, ["file-exists", "file-is-readable"]}
                                                               ]}.
%% @doc When using certificate based TLS, the bridge will attempt to verify the 
%% hostname provided in the remote certificate matches the host/address being 
%% connected to. This may cause problems in testing scenarios, so this option 
%% may be enabled to disable the hostname verification.
%%
%% Setting this option to true means that a malicious third party could 
%% potentially inpersonate your server, so it should always be disabled in 
%% production environments.
{mapping, "vmq_bridge.ssl.$name.insecure", "vmq_bridge.config", [
                                                                {default, off},
                                                                {datatype, flag},
                                                                {include_default, "sbr0"},
                                                                {commented, off}
                                                               ]}. 
%% @doc Configure the TLS protocol version (tlsv1, tlsv1.1, or tlsv1.2) to be 
%% used for this bridge.
{mapping, "vmq_bridge.ssl.$name.tls_version", "vmq_bridge.config", [
                                                                   {default, 'tlsv1.2'},
                                                                   {datatype, atom},
                                                                   {include_default, "sbr0"},
                                                                   {commented, 'tlsv1.2'}
                                                                  ]}. 
%% @doc Pre-shared-key encryption provides an alternative to certificate based 
%% encryption. This option specifies the identity used. 
{mapping, "vmq_bridge.ssl.$name.identity", "vmq_bridge.config", [
                                                                {default, ""},
                                                                {datatype, string},
                                                                {include_default, "sbr0"},
                                                                {commented, ""}
                                                               ]}. 
%% @doc Pre-shared-key encryption provides an alternative to certificate based 
%% encryption. This option specifies the shared secret used in hexadecimal
%% format without leading '0x'. 
{mapping, "vmq_bridge.ssl.$name.psk", "vmq_bridge.config", [
                                                           {default, ""},
                                                           {datatype, string},
                                                           {include_default, "sbr0"},
                                                           {commented, ""}
                                                          ]}. 
%% @doc Setting the depth for intermediate chains
{mapping, "vmq_bridge.ssl.$name.depth", "vmq_bridge.config", [
                                                           {default, 1},
                                                           {datatype, integer},
                                                           hidden
                                                          ]}.

%% @doc Allow the bridge to open SSL connections to remote broker with wildcard certs
{mapping, "vmq_bridge.ssl.$name.customize_hostname_check", "vmq_bridge.config", [
                                                         {datatype, {enum, ['https']}},
                                                            {default, 'https'},
                                                            {commented, on}
                                                        ]}.
%% @doc For outgoing bridges, let us know whether the other endpoint is
%% 'inet' or 'inet6' (an IPv4 or an IPv6 address)
{mapping, "vmq_bridge.ssl.$name.inet_version", "vmq_bridge.config", [
                                                         {datatype, {enum, ['inet', 'inet6']}},
                                                            {default, 'inet'},
                                                            hidden
                                                        ]}.

%{mapping, "vmq_bridge.registry_mfa", "vmq_bridge.registry_mfa", 
% [
%  {default, "{vmq_reg,direct_plugin_exports,[vmq_bridge]}"},
%  {datatype, string},
%  hidden
% ]}. 

%{translation, "vmq_bridge.registry_mfa", 
% fun(Conf) ->
%         S = cuttlefish:conf_get("vmq_bridge.registry_mfa", Conf),
%         {ok, T, _} = erl_scan:string(S ++ "."),
%         {ok, Term} = erl_parse:parse_term(T),
%         Term
% end}.

{translation, "vmq_bridge.config", 
 fun(Conf) ->
         BoolVal = fun(_, B) when is_boolean(B) -> B end,
         StringVal = fun(_, "") -> undefined;
                        (_, S) when is_list(S) -> S; 
                        (_, undefined) -> undefined end,
         IntVal = fun(_, I) when is_integer(I) -> I;
                     (_, undefined) -> undefined end,
         IntAtomVal = fun(_, IAtom) when is_atom(IAtom) ->
                              list_to_integer(atom_to_list(IAtom));
                         (_, undefined) -> undefined
                        end,
         AtomVal = fun(_, A) when is_atom(A) -> A end,
         ClientIdVal = fun(Name, "auto") -> 
                               {ok, HostName} = inet:gethostname(),
                               lists:flatten([HostName, ".", Name]);
                          (_, L) when is_list(L) -> L
                       end,

         TopicVal = fun
                        (_, TopicString) when is_list(TopicString) ->
                            TTopicString = re:replace(TopicString, "\\*", "#", [{return, list}]),
                            Directions = ["out", "in", "both"],
                            QoSs = ["0", "1", "2"],
                            [Pattern|Rest0] = string:tokens(TTopicString, " "),
                            case Rest0 of
                                [MaybeDirection|Rest1] ->
                                    {Direction, Rest2} =
                                    case lists:member(MaybeDirection, Directions) of
                                        true -> 
                                            {list_to_atom(MaybeDirection), Rest1};
                                        false ->
                                            {out, Rest0}
                                    end,
                                    {QoS, Rest3} =
                                    case Rest2 of
                                        [] ->
                                            {0, []};
                                        [MaybeQoS|Rest4] ->
                                            case lists:member(MaybeQoS, QoSs) of
                                                true ->
                                                    {list_to_integer(MaybeQoS), Rest4};
                                                false ->
                                                    {0, Rest2}
                                            end
                                    end,
                                    {Retain, Rest5} =
                                    case Rest3 of
                                        [] ->
                                            {false, []};
                                        [MaybeRetain|Rest6] ->
                                            case lists:member(MaybeRetain, ["retain"]) of
                                                true ->
                                                    {true, Rest6};
                                                false ->
                                                    {false, Rest3}
                                            end
                                    end,

                                    Empty = fun("$empty") ->
                                                    "";
                                               (V) -> V
                                            end,
                                    case Rest5 of
                                        [] ->
                                            {Pattern, Direction, QoS, Retain, "", ""};
                                        [LocalPrefix, RemotePrefix] ->
                                            {Pattern, Direction, QoS, Retain,
                                             Empty(LocalPrefix), Empty(RemotePrefix)};
                                        _ ->
                                            cuttlefish:invalid("should be a string of the form 'pattern [[[ out | in | both ] qos-level] retain] local-prefix remote-prefix]'")
                                    end;
                                [] ->
                                    {Pattern, out, 0, false, "", ""}
                            end;
                        (_, _) ->
                            cuttlefish:invalid("should be a string of the form 'pattern [[[ out | in | both ] qos-level] local-prefix remote-prefix]'")
                    end,

         Mappings = ["cleansession", "client_id", "mountpoint", "keepalive_interval", "segment_size", "outgoing_batch_size",
                    "persistent_queue", "queue_dir",
                    "username", "password", "restart_timeout", "try_private", "topic",
                     "max_outgoing_buffered_messages", "pubrel_queue_ratio", "mqtt_version", "inet_version"],
                    %% SSL specific
         SSLMapps = ["cafile", "certfile", "keyfile", "insecure", "tls_version", "identity", "psk", "depth", 
         "customize_hostname_check", "sni", "inet_version"],
         F = fun(Prefix, Suffix, Validator) ->
                     %% get the name value pairs
                     Prefix3 = lists:flatten([Prefix, ".$name"]),
                     Res =
                         [begin
                              case lists:prefix("topic", Suffix) of
                                  true ->
                                      BrConfigs =
                                          [begin
                                               P = lists:flatten(
                                                     [Prefix, ".", Name, ".topic.", integer_to_list(I)]
                                                    ),
                                               cuttlefish:conf_get(P, Conf, undefined)
                                           end || I <- lists:seq(1,100)],                                     
                                           AddrPortName = lists:flatten([Name, ":", AddrPort]),
                                      {AddrPortName, {topics, [Validator(Name, C) || C <- BrConfigs, C /= undefined]}};
                                  false ->
                                      Prefix4 = lists:flatten([Prefix, ".", Name, ".", Suffix]),
                                      Val = Validator(Name, cuttlefish:conf_get(Prefix4, Conf, undefined)),
                                     AddrPortName = lists:flatten([Name, ":", AddrPort]),
                                    {AddrPortName, {list_to_atom(Suffix), Val}}
                              end
                          end
                          || {[_, _, Name], AddrPort} <- lists:filter(
                                                           fun({K, _V}) ->
                                                                   cuttlefish_variable:is_fuzzy_match(K, string:tokens(Prefix3, "."))
                                                           end, Conf), not lists:member(Name, Mappings ++ SSLMapps)],
                     %% Remove duplicates as a value is generated for
                     %% each setting on a bridge definition.
                     lists:usort(Res)
             end,

         MZip = fun([H|_] = ListOfLists) ->
                        Size = length(H), %% get default size
                        ListOfLists = [L || L <- ListOfLists, length(L) == Size],
                        [
                           lists:reverse(
                             lists:foldl(
                               fun(L, Acc) ->
                                       [lists:nth(I, L)|Acc]
                               end, [], ListOfLists))
                         || I <- lists:seq(1, Size)]
                end,

         {TCPIPs, TCPCleanSessions} = lists:unzip(F("vmq_bridge.tcp", "cleansession", BoolVal)),
         {TCPIPs, TCPClientIds} = lists:unzip(F("vmq_bridge.tcp", "client_id", ClientIdVal)),
         {TCPIPs, TCPMountPoint} = lists:unzip(F("vmq_bridge.tcp", "mountpoint", StringVal)),
         {TCPIPs, TCPKeepAlive} = lists:unzip(F("vmq_bridge.tcp", "keepalive_interval", IntVal)),
         {TCPIPs, TCPPersistentQueue} = lists:unzip(F("vmq_bridge.tcp", "persistent_queue", BoolVal)),
         {TCPIPs, TCPQueueDir} = lists:unzip(F("vmq_bridge.tcp", "queue_dir", StringVal)),
         {TCPIPs, TCPSegmentSize} = lists:unzip(F("vmq_bridge.tcp", "segment_size", IntVal)),
         {TCPIPs, TCPOutBatchSize} = lists:unzip(F("vmq_bridge.tcp", "outgoing_batch_size", IntVal)),
         {TCPIPs, TCPUserNames} = lists:unzip(F("vmq_bridge.tcp", "username", StringVal)),
         {TCPIPs, TCPPasswords} = lists:unzip(F("vmq_bridge.tcp", "password", StringVal)),
         {TCPIPs, TCPRestartTimeouts} = lists:unzip(F("vmq_bridge.tcp", "restart_timeout", IntVal)),
         {TCPIPs, TCPTryPrivates} = lists:unzip(F("vmq_bridge.tcp", "try_private", BoolVal)),
         {TCPIPs, TCPMQTTProtos} = lists:unzip(F("vmq_bridge.tcp", "mqtt_version", IntAtomVal)),
         {TCPIPs, TCPMaxBuffer} = lists:unzip(F("vmq_bridge.tcp", "max_outgoing_buffered_messages", IntVal)),
         {TCPIPs, TCPQueueRatio} = lists:unzip(F("vmq_bridge.tcp", "pubrel_queue_ratio", IntVal)),
         {TCPIPs, TCPTopics} = lists:unzip(F("vmq_bridge.tcp", "topic", TopicVal)),
         {TCPIPS, TCPInets} = lists: unzip(F("vmq_bridge.tcp", "inet_version", AtomVal)),

         {SSLIPs, SSLCleanSessions} = lists:unzip(F("vmq_bridge.ssl", "cleansession", BoolVal)),
         {SSLIPs, SSLClientIds} = lists:unzip(F("vmq_bridge.ssl", "client_id", ClientIdVal)),
         {SSLIPs, SSLMountPoint} = lists:unzip(F("vmq_bridge.ssl", "mountpoint", StringVal)),
         {SSLIPs, SSLKeepAlive} = lists:unzip(F("vmq_bridge.ssl", "keepalive_interval", IntVal)),
         {SSLIPs, SSLPersistentQueue} = lists:unzip(F("vmq_bridge.ssl", "persistent_queue", BoolVal)),
         {SSLIPs, SSLQueueDir} = lists:unzip(F("vmq_bridge.ssl", "queue_dir", StringVal)),
         {SSLIPs, SSLSegmentSize} = lists:unzip(F("vmq_bridge.ssl", "segment_size", IntVal)),
         {SSLIPs, SSLOutBatchSize} = lists:unzip(F("vmq_bridge.ssl", "outgoing_batch_size", IntVal)),
         {SSLIPs, SSLUserNames} = lists:unzip(F("vmq_bridge.ssl", "username", StringVal)),
         {SSLIPs, SSLPasswords} = lists:unzip(F("vmq_bridge.ssl", "password", StringVal)),
         {SSLIPs, SSLRestartTimeouts} = lists:unzip(F("vmq_bridge.ssl", "restart_timeout", IntVal)),
         {SSLIPs, SSLTryPrivates} = lists:unzip(F("vmq_bridge.ssl", "try_private", BoolVal)),
         {SSLIPs, SSLMQTTProtos} = lists:unzip(F("vmq_bridge.ssl", "mqtt_version", IntAtomVal)),
         {SSLIPs, SSLMaxBuffer} = lists:unzip(F("vmq_bridge.ssl", "max_outgoing_buffered_messages", IntVal)),
         {SSLIPs, SSLQueueRatio} = lists:unzip(F("vmq_bridge.ssl", "pubrel_queue_ratio", IntVal)),
         {SSLIPs, SSLTopics} = lists:unzip(F("vmq_bridge.ssl", "topic", TopicVal)),

         % SSL
         {SSLIPs, SSLCAFiles} = lists:unzip(F("vmq_bridge.ssl", "cafile", StringVal)),
         {SSLIPs, SSLCertFiles} = lists:unzip(F("vmq_bridge.ssl", "certfile", StringVal)),
         {SSLIPs, SSLKeyFiles} = lists:unzip(F("vmq_bridge.ssl", "keyfile", StringVal)),
         {SSLIPs, SSLInsecures} = lists:unzip(F("vmq_bridge.ssl", "insecure", BoolVal)),
         {SSLIPs, SSLVersions} = lists:unzip(F("vmq_bridge.ssl", "tls_version", AtomVal)),
         {SSLIPs, SSLIdentities} = lists:unzip(F("vmq_bridge.ssl", "identity", StringVal)),
         {SSLIPs, SSLPSKs} = lists:unzip(F("vmq_bridge.ssl", "psk", StringVal)),
         {SSLIPS, SSLDepths} = lists:unzip(F("vmq_bridge.ssl", "depth", IntVal)),
         {SSLIPS, SSLALLOWWCards} = lists:unzip(F("vmq_bridge.ssl", "customize_hostname_check", AtomVal)),
         {SSLIPS, SSLSNIs} = lists:unzip(F("vmq_bridge.ssl", "sni", StringVal)),
         {SSLIPS, SSLInets} = lists: unzip(F("vmq_bridge.ssl", "inet_version", AtomVal)),


         TCP = lists:zip(TCPIPs, MZip([TCPCleanSessions, 
                                       TCPClientIds, 
                                       TCPMountPoint,
                                       TCPKeepAlive,
                                       TCPSegmentSize,
                                       TCPPersistentQueue,
                                       TCPQueueDir,
                                       TCPOutBatchSize, 
                                       TCPRestartTimeouts,
                                       TCPUserNames, 
                                       TCPPasswords,
                                       TCPTryPrivates,
                                       TCPMQTTProtos,
                                       TCPMaxBuffer,
                                       TCPQueueRatio,
                                       TCPTopics,
                                       TCPInets])),

         SSL = lists:zip(SSLIPs, MZip([SSLCleanSessions, 
                                       SSLClientIds,
                                       SSLMountPoint, 
                                       SSLKeepAlive, 
                                       SSLSegmentSize,
                                       SSLPersistentQueue,
                                       SSLQueueDir,
                                       SSLOutBatchSize,
                                       SSLRestartTimeouts,
                                       SSLUserNames, 
                                       SSLPasswords,
                                       SSLTryPrivates,
                                       SSLMQTTProtos,
                                       SSLMaxBuffer,
                                       SSLQueueRatio,
                                       SSLTopics,
                                       SSLCAFiles, 
                                       SSLCertFiles,
                                       SSLKeyFiles, 
                                       SSLInsecures, 
                                       SSLVersions, 
                                       SSLIdentities, 
                                       SSLPSKs,
                                       SSLDepths,
                                       SSLALLOWWCards,
                                       SSLSNIs,
                                       SSLInets])),
         DropUndef = fun(L) ->
                             [{K, [I || {_, V} = I  <- SubL, V /= undefined]} || {K, SubL} <- L]
                     end,
         {DropUndef(TCP), DropUndef(SSL)}
 end
}.

{validator, "file-exists", "file does not exist",
 fun("") ->
         %% "" is used as the default value in most places, we should
         %% not error out because of that.
         true;
    (Name) -> vmq_schema_util:file_exists(Name)
 end}.
{validator, "file-is-readable", "file is not readable, check permissions",
 fun("") ->
         %% "" is used as the default value in most places, we should
         %% not error out because of that.
         true;
    (Name) -> vmq_schema_util:file_is_readable(Name)
 end}.

{mapping, "vmq_bridge.clique_lead_line", "vmq_bridge.clique_lead_line",
 [{datatype, string},
  hidden,
  {default, "    bridge      Manage MQTT bridges\n"}]}.
